/*
 * Created on Oct 18, 2004
 *
 * TODO To change the template for this generated file go to
 * Window - Preferences - Java - Code Style - Code Templates
 */
package MethodCatcher;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;

/**
 * @author Jayanth Recherla
 *
 * TODO To change the template for this generated type comment go to
 * Window - Preferences - Java - Code Style - Code Templates
 */
public class MethodCatcher {
		
	 private InvocationHandler invocationHandler_ = new InvocationHandler() {
        public Object invoke( final Object proxy, final Method method, final Object[] args) {
            final String methodName = method.getName();
            final Object result;
            if( methodName.equals("hashCode") ) {
                result =  new Integer(1);
            }
            else if( methodName.equals("equals") && args.length == 1 ) {
                result = new Boolean(this == args[0]);
            }
            else {
                synchronized(MethodCatcher.this) {
                	if( methodName.startsWith("set") && methodName.endsWith("State") ) 
                		setState(methodName.substring(3, methodName.lastIndexOf("State")));  
                	methodList.add(new MethodObject(method,args));
                }
                result = null;
            }
            return result;
        }
    };

	private List methodList = new ArrayList();
	private List argsList = new ArrayList();
	private String state = null;
	
	public MethodCatcher(Object obj){
		
	}
	
	public MethodCatcher(){
		
	}
	
	
	public void listenTo( final Object object ) throws IllegalAccessException, InvocationTargetException 
	{
		final Method methods[] = object.getClass().getMethods();
		String methodName;
		Class  parameterClasses[];
		final Map addMethods = new HashMap(89);

		int i;
		for( i=0; i<methods.length; i++ ) {
			methodName = methods[i].getName();
				if( methodName.startsWith("set") && methodName.endsWith("Interface") ) {
					parameterClasses = methods[i].getParameterTypes();
				if( parameterClasses.length == 1 ) {
					addMethods.put( methods[i], parameterClasses[0] );
				}
			}
		}

		Map.Entry entry;
		Object proxy;
		Method method;
		
		final Iterator iterator = addMethods.entrySet().iterator();
		while( iterator.hasNext() ) {
			entry = (Map.Entry)iterator.next();
			method = (Method)entry.getKey();
			proxy = getMethodImplementation( (Class)entry.getValue() );
			method.invoke( object, new Object[]{proxy} );
		}
    }
	
	public Object getMethodImplementation( final Class clazz ) {
        return Proxy.newProxyInstance( getClass().getClassLoader(),
                                       new Class[]{ clazz },
                                       invocationHandler_ );
    }
	
	public Collection getMethods(){
		ArrayList methods = new ArrayList();
		Iterator i = methodList.iterator() ;
		while(i.hasNext()) {
			MethodObject methodObject = (MethodObject) i.next() ;
			methods.add(methodObject.getMethod());
		}
		return methods;
	}
	
	public Collection getArgs(){
		ArrayList args = new ArrayList();
		Iterator i = methodList.iterator() ;
		while(i.hasNext()) {
			MethodObject methodObject = (MethodObject) i.next() ;
			args.add(methodObject.getArgs());
		}
		return args;
	}
	
	public Method getMethodAt(int index){
		MethodObject methodObject = (MethodObject) methodList.get(index);
		return methodObject.getMethod() ;
	}
	
	public MethodObject getMethodObjectAt(int index){
		return (MethodObject) methodList.get(index);		
	}
	
	public Object[] getArgsPassedAt(int index){
		MethodObject methodObject = (MethodObject) methodList.get(index);
		return methodObject.getArgs() ;
	}
	
	public int size(){
		return methodList.size() ;
	}
	
	public int getMethodCount(){
		return methodList.size() ;
	}
	
	public Method getLastMethod() {
		MethodObject methodObject = (MethodObject)methodList.get(getMethodCount() - 1);
		return methodObject.getMethod() ;
	}
	
	public Method getFirstMethod() {
		MethodObject methodObject = (MethodObject)methodList.get(0);
		return methodObject.getMethod() ;
	}
	
	public boolean assertLastMethod(Method method) {
		Method lastMethod = this.getLastMethod() ;
		return method.equals( lastMethod ) ;
	}
	
	public boolean assertMethodAt(int index, Method method) {
		Method methodAt = this.getMethodAt( index );
		return methodAt.equals( method );
	}
	
	public void setState(String state) {
		this.state = state;
	}
	public String getState(){
		return state;
	}
	public boolean assertCurrentState(String state) {
		return state.equals(this.state);
	}
	
}


